﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.Xml.Schema;
using System.Xml.Serialization;
using UblSharp.XmlDigitalSignature;

namespace UblSharp.Tests.Generator
{
    class Program
    {
        static void Main(string[] args)
        {
            GenerateUblTests();

            Console.WriteLine("Done.");
            Console.ReadKey();
        }

        private static void GenerateUblTests()
        {
            var ublXmlInputDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"..\..\UblSharp.Tests\Samples");
            var cSharpOutputDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"..\..\UblSharp.Tests\Samples");

            var ublDocumentTypesDictionary = typeof(BaseDocument).Assembly.GetTypes()
                .Where(t => t.BaseType == typeof(BaseDocument))
                // .Concat(new[] { typeof(XmlSignatureType) })
                .ToDictionary(k => GetQualifiedName(k).ToString(), v => v);

            var docsToConvert = new List<XmlToCs>();
            var xmlSamplesDir = new DirectoryInfo(ublXmlInputDir);
            foreach (var xmlFileInfo in xmlSamplesDir.GetFiles("*.xml"))
            {
                var doc = XDocument.Load(xmlFileInfo.FullName);
                if (ublDocumentTypesDictionary.ContainsKey(doc.Root.Name.ToString()))
                {
                    var docType = ublDocumentTypesDictionary[doc.Root.Name.ToString()];
                    var xmlToCs = new XmlToCs(doc.Root, docType, xmlFileInfo.Name);
                    docsToConvert.Add(xmlToCs);
                }
                else
                {
                    Console.WriteLine($"Warning: Skip {xmlFileInfo.Name} of type {doc.Root.Name}");
                }
            }
            // debug sample
            //var d = docsToConvert.Where(n => n.IdentifierName == "UBLOrderResponse21Example").Single();
            //d.GenerateClass();
            //d.SaveToDir(cSharpOutputDir);
            //return;
            var sb = new StringBuilder();
            using (var sw = new StringWriter(sb))
            {
                foreach (var xmlToCs in docsToConvert)
                {
                    Console.WriteLine($"{xmlToCs.IdentifierName} \t{xmlToCs.CSharpFilename}");
                    xmlToCs.GenerateSourceCode();
                    xmlToCs.SaveToDir(cSharpOutputDir);
                    GenerateTestMethod(sw, xmlToCs);
                }
            }

            using (var writer = File.CreateText(Path.Combine(cSharpOutputDir, "SampleTests.partial.cs"))) // paste content into cs file later
            {
                writer.Write(testFileTemplate, sb);
            }
        }

        private static void ValidationHandler(object sender, ValidationEventArgs e)
        {
            Console.WriteLine($"{e.Severity}: {e.Message}");
            if (e.Severity == XmlSeverityType.Error)
            {
                throw e.Exception;
            }
        }

        /// <summary>
        /// 0-Instance, 1-xmlfile
        /// </summary>
        private static string testDocumentTemplate = @"        [UblSampleFact(""{1}"")]
        public void {0}Test()
        {{
            bool areEqual = TestDocument(""{1}"", Samples.{0}.Create);";
        private static string testFileTemplate = @"// Generated by UblSharp.Tests.Generator
using System;
using Xunit;
using UblSharp.Tests.Util;

namespace UblSharp.Tests
{{
    public partial class SampleTests
    {{
{0}
    }}
}}";

        private static void GenerateTestMethod(TextWriter writer, XmlToCs xmlToCs)
        {
            writer.WriteLine(testDocumentTemplate, xmlToCs.IdentifierName, xmlToCs.XmlFilename);
            if (xmlToCs.HasExtensionsOrSignature)
            {
                // writer.WriteLine("            Assert.Inconclusive(\"Signatures/extensions not implemented!\");");
            }
            else
            {
                writer.WriteLine("            Assert.True(areEqual, \"Written {0} differs from the one read\");", xmlToCs.DocType.Name);
            }
            writer.WriteLine("        }\r\n\r\n");
        }

        private static XName GetQualifiedName(Type key)
        {
            var typeName = key.Name;
            var att = key.CustomAttributes.Single(a => a.AttributeType == typeof(XmlTypeAttribute));
            var ns = att.NamedArguments.Single(n => n.MemberName == "Namespace").TypedValue.Value as string;
            var name = att.ConstructorArguments.FirstOrDefault().Value as string ??
                att.NamedArguments.FirstOrDefault(n => n.MemberName == "TypeName").TypedValue.Value as string ??
                key.Name;
            return XName.Get(name, ns);
        }
    }
}
